'use strict'; 
import { isDOM } from '../utils/is-dom';
import { addClass } from '../utils/add-class';
import { removeClass } from '../utils/rm-class';
import { query, queryAll } from '../utils/dom-query';

// 常用的DOM方法包装
class Q {
    constructor(nodelist) { 
        this._natived = nodelist; // 原生节点
        this._index = 0; // 节点在同级中的下标
    } 

    get index (){
        return this._index;
    } 
    set index (index){
        this._index = index;
    }
    get natived (){
        return this._natived;
    }

    /**
     * ********************************************* Classlist操作
     * classlist存在兼容问题 IE、IE Mobile、Opera Mini不完全支持，不支持add、remove方法
     * 下面提供的是HTML5新的API包装方法，在以后不再考虑兼容上述浏览器时，可以直接打开使用
     * 同时也提供了相应的兼容处理方法
     **/ 

    /* 
    // 新增className
    addClass (className){
        this.natived.classList.add(className);
    }
    */ 
    
    /* 
    // 是否包含className
    hasClass (className){
        this.natived.classList.contains(className);
    }
    */ 
   
    /* 
    // 删除className
    removeClass (className) { 
        this.natived.classList.remove(className);
    }
    */ 

    //新增class 
    _addClass(className) { 

        addClass(this.natived, className);

        // const natived = this.natived, 
        //       original = natived.className;

        // if (original.length === 0) { 
        //     natived.className = className;
        //     return;
        // }

        // if (this.hasClass(className)){
        //     return;
        // }

        // natived.className = original + ' ' + className;
    }
    // 是否含有指定class
    hasClass (className){ 
        return this.natived.className.match(new RegExp('(^|\\s)' + className + '(\\s|$)'));
    }
    // 删除指定class
    removeClass(className) {
        removeClass(this.natived, className);
        // const natived = this.natived;
        // const original = natived.className;
        
        // if (original.length === 0){
        //     return;
        // }

        // if (original == className) {
        //     this.natived.className = '';
        //     return;
        // }

        // if (this.hasClass(className)) {
        //     this.natived.className = original.replace((new RegExp('(^|\\s)' + className + '(\\s|$)')), '');
        // }
    } 

    /**
     * ********************************************* HTML Attr 操作
     * data-* HTML5自定义属性单独实现，避免使用原生的dataset
     * 注意: HTML元素属性仅仅支持a-z、A-Z、0-9、_这些字符
     **/ 
    // 增加、删除、更改属性值
    attr (name, value){ 
        switch (typeof name){
            // 1> 当参数name为string类型时
            case 'string': 
                // 1.1> 当参数value不存在时，视为获取属性
                if (!value) {
                    return this.natived.getAttribute(name.toLowerCase())
                };
                // 1.2> 与1.1相反 
                this.natived.setAttribute(name.toLowerCase(), value);
            break;
            // 2> 当参数name为对象时，视为批量设置属性，忽略第二个参数
            case 'object':
                for (let key in name) {
                    // 2.1> 遍历name对象，key值为字符串时，视为设置属性
                    if ('string' === typeof key) { 
                        const keyValue = name[key];
                        const newKey = key.toLowerCase();
                        if (keyValue) {
                            const original = this.natived.getAttribute(newKey);

                            if (original && original !== '') {
                                // 该属性存在时值时，当不包含当前值时，将新值keyValue加到已有值的后面
                                if (keyValue && !original.match(new RegExp('(^|\\s)' + keyValue + '(\\s|$)'))){
                                    this.natived.setAttribute(newKey, original + ' ' + keyValue);
                                }
                            } else {
                                this.natived.setAttribute(newKey, keyValue);
                            }
                        }
                    }
                }
            break;
            default: break;
        }
    }
    // 删除指定的
    removeAttr (...argv) {
        argv.length > 0 && argv.forEach(attr=> this.natived.removeAttribute(attr));
    }
    /** 
     * 自定义属性data-*操作
     * IE 10-, IE Mobile 10-, Opera Mini All 部分支持
     * 部分支持是指能够使用data-*属性并使用getAttribute访问它们 
     **/
    data (name, value){
        if (/^data-\w+$/.test(name)) {
            if (value) {
                this.attr(name, value);
            }
            return this.attr(name);
        }
        switch (typeof name){
            case 'string': 
            case 'boolean':  
            case 'number':
                name = String(name).toLowerCase();
                if (value) {
                    return this.attr('data-'+name, value);
                }
                return this.attr('data-'+name);
            break;
            case 'object':
                for (let key in name) {
                    this.attr( 'data-' + key.toLowerCase(), name[key])
                }
            break;
            default : break;
        }
    }

   /**
    * ********************************************* HTML5 DOM 增删改
    * 
    **/
   // 清空元素自身的内容
    empty(){ 
        this.natived.innerHTML = '';
    }
    // 删除元素自身
    remove(){
        this.natived.parent.removeChild(this);
    }
    // 将指定元素追加到元素末尾
    append(obj){ 
        if (isDOM(obj)) {
            this.natived.appendChild(obj);
        }
    }
    // 将指定元素前置元素子元素首位
    prepend(obj){
        if (isDOM(obj)) {
            if (this.childrenCount > 0) {
                this.natived.insertBefore(obj, this.first);
            } else {
                this.natived.appendChild(obj);
            }
        } 
    } 
    // 给当前元素的之前插入指定的节点
    beofre (obj){
        if (isDOM(obj)) {
            this.parent.insertBefore(obj,this); 
        }
    }
    // 给当前元素的之后插入指定的节点
    after(obj){
        if(this.next) {
            this.parent.insertBefore(obj,this.next);
        } else {
            this.parent.appendChild(obj);
        } 
    }

    /**
     * ********************************************* 子元素过滤
     * 
     * 我们一般可以采用DFS（深度优先遍历）和BFS（广度优先遍历）来遍历DOM树
     * DFS总是先进入下一级节点，只有当下一级没有未遍历的子节点时才会进入到当前层级的其它节点
     * BFS则总是先遍历当前层级的所有节点，只有当当前层级所有节点都遍历结束后才会进入下一层级。
     * 
     * https://segmentfault.com/a/1190000009112326
     * https://www.cnblogs.com/joyeecheung/p/4168521.html 
     * 
     * 以下Wathwg、DOM4、W3C表现一致
     * node.firstElementChild
     * node.lastElementChild
     * node.previousElementSibling
     * node.nextElementSibling
     * node.childElementCount 约等于 node.children.length
     * 以上方法 IE 9+ 兼容 
     **/
    // 当前元素集合中的指定下标的元素
    eq(index){
        return Math.abs(index) < this.childrenCount ? this.children[index >= 0 ? index : (~index)] : void 0;
    }
    // 当前元素集合中的第一个元素
    get first(){ 
        return this.children[0];
    }
    // 当前元素集合中的最后一个元素
    get last(){ 
        return this.children[this.childrenCount-1];
    }
    // 当前元素集合中的元素的所有子元素
    // IE8- 的 children 会包含 comment node。
    get children(){
        return this.natived.children;
    }
    // 当前元素的子元素的个数 
    get childrenCount(){
        return this.children.length;
    }
    
    /**
     * ********************************************* 同层级元素过滤
     * 当前元素集合中的元素的所有父元素
     * IE8- 中 parentNode 有 bug  
     **/  
    // 在只需要 Element 的场景下，选择 parentElement
    get parent(){
        return this.natived.parentElement;
    }
    // 当前元素的下一个元素
    get next (){
        return this.natived.nextElementSibling;
    }
    // 当前元素的上一个元素
    get prev(){ 
        return this.natived.previousElementSibling;
    }

    /**
     * ********************************************* 获取元素的HTML、text 
     * 
     **/ 
    // 获取、设置当前元素的HTML内容
    html (obj) {
        return !obj ? this.natived.innerHTML : this.natived.innerHTML = String(obj);
    }
    text (obj) {
        return !obj ? this.natived.innerText : this.natived.innerText = String(obj);        
    }
    val (obj) {
        return !obj ? this.natived.value : this.natived.value = String(obj);
    }
}

// 查询
export function dQuery (exp, ctx) {
    if (isDOM(exp)) {
        return new Q(exp);
    }
    if (Array.isArray(exp)) {
        // if (exp.__Q__) {
        //     return exp;
        // }
        return exp;
    }
    let nodelist;
    if (exp instanceof HTMLCollection || exp instanceof NodeList) {
        nodelist = exp;
    }else {
        nodelist = /^#\w+/.test(exp)? query(exp,ctx): queryAll(exp,ctx);
    }

    if (!nodelist.length) {
        return new Q(nodelist);
    }

    const list = [];
    for (let i=0, len = nodelist.length; i< len; i++) {
        const node = nodelist[i]
        const q = new Q(node);
        q.index = i;
        list.push(q);
    }
     
    // list.__Q__ = 1;
    return list;
}
